package com.hyd.dao;

import com.hyd.dao.database.ExecutorFactory;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * 管理数据源配置
 *
 * @author yiding.he
 */
public class DataSourceManager {

    private Map<String, DataSource> dataSources = new HashMap<String, DataSource>();

    private Map<String, ExecutorFactory> executorFactories = new HashMap<String, ExecutorFactory>();

    public Map<String, DataSource> getDataSources() {
        return dataSources;
    }

    @SuppressWarnings("UnusedDeclaration")
    public void setDataSources(Map<String, DataSource> dataSources) {
        this.dataSources = dataSources;
    }

    /**
     * 根据数据源名称获取或创建一个 DAO 对象
     *
     * @param dsName 数据源名称
     *
     * @return DAO 对象
     */
    public DAO getDAO(String dsName) {
        return getDAO(dsName, false);
    }

    /**
     * 根据数据源名称获取或创建一个 DAO 对象
     *
     * @param dsName     数据源名称
     * @param standAlone 是否独立于当前事务之外
     *
     * @return DAO 对象
     */
    public DAO getDAO(String dsName, boolean standAlone) {
        DAO dao = new DAO(dsName, standAlone);
        dao.setExecutorFactory(getExecutorFactory(dsName));
        return dao;
    }

    /**
     * 根据数据源名称获取或创建一个 DAO 对象
     *
     * @param dsName       数据源名称
     * @param standAlone   是否独立于当前事务之外
     * @param subclassType 用于包装的子类型，该类必须实现构造方法 (String, boolean)。
     *
     * @return DAO 对象
     */
    public <T extends DAO> T getDAO(String dsName, boolean standAlone, Class<T> subclassType) {
        try {
            T dao = subclassType.getConstructor(String.class, Boolean.TYPE).newInstance(dsName, standAlone);
            dao.setExecutorFactory(getExecutorFactory(dsName));
            return dao;
        } catch (Exception e) {
            throw new DAOException(e);
        }
    }

    /**
     * 根据数据源名称获取 ExecutorFactory 对象
     *
     * @param dsName 数据源名称
     *
     * @return ExecutorFactoty 对象
     */
    private synchronized ExecutorFactory getExecutorFactory(String dsName) {

        if (dsName == null) {
            return null;
        }

        if (!dataSources.containsKey(dsName)) {
            throw new IllegalArgumentException("Unknown data source '" + dsName + "'");
        }

        if (executorFactories.containsKey(dsName)) {
            return executorFactories.get(dsName);
        }

        DataSource dataSource = getDataSources().get(dsName);
        ExecutorFactory factory = new ExecutorFactory(dsName, dataSource);

        executorFactories.put(dsName, factory);
        return factory;
    }
}
